在前幾天,我們已經認識了 Node.js 的基礎與專案骨架。
今天要進入一個非常重要的主題:模組系統 (Modules)。
模組是程式碼的組裝單位,幫助我們拆分功能、重複利用,避免程式碼一坨混在一起。
在 Node.js 世界裡,目前存在 兩套模組系統:
CommonJS (CJS)
Node.js 在 2009 年誕生時,瀏覽器還沒有標準化的模組語法。
為了管理大型程式碼,社群制定了 CommonJS,使用 require / module.exports。
npm 上大量套件都是基於 CJS 開發的。
ES Modules (ESM)
2015 年(ES6)JavaScript 終於有了官方模組系統:import / export。
它支援靜態分析、tree-shaking,並與瀏覽器保持一致。
👉CJS 是 Node.js 的傳統,ESM 是未來趨勢。
Node.js 提供幾種方式來決定 .js 檔案的解讀方式:
預設:沒有特別設定 → .js 當成 CJS。
使用副檔名:
.cjs → 永遠視為 CJS.mjs → 永遠視為 ESM設定 package.json:
{
  "type": "module"
}
這樣專案裡的 .js 會預設是 ESM,要寫 CJS 就得用 .cjs。
| 項目 | CommonJS (CJS) | ES Modules (ESM) | 
|---|---|---|
| 副檔名 | .cjs/.js(預設) | .mjs或.js(需package.json設定"type": "module") | 
| 匯入 | const x = require('./x') | import x from './x.js' | 
| 匯出 | module.exports = ...或exports.a = ... | export default ...或export const a = ... | 
| 載入方式 | 同步(執行時解析) | 非同步(編譯期分析,可用 top-level await) | 
| 相容性 | 舊專案與大部分套件 | 新專案趨勢,與瀏覽器標準一致 | 
// math.cjs
function add(a, b) {
  return a + b;
}
function subtract(a, b) {
  return a - b;
}
module.exports = { add, subtract };
// app.cjs
const { add, subtract } = require('./math.cjs');
console.log(add(3, 5));      // 8
console.log(subtract(10, 4)); // 6
方式一:default export
// math.mjs
export default function add(a, b) {
  return a + b;
}
// app.mjs
import add from './math.mjs';
console.log(add(2, 3)); // 5
方式二:named export
// math.mjs
export function add(a, b) {
  return a + b;
}
export function subtract(a, b) {
  return a - b;
}
// app.mjs
import { add, subtract } from './math.mjs';
console.log(add(4, 6));      // 10
console.log(subtract(9, 2)); // 7
📌 注意:ESM 匯入相對路徑時,副檔名必須寫 .js 或 .mjs,否則會報錯。
在實際開發專案中,常會遇到「CJS 與 ESM 混用」的情況:
在 ESM 專案裡引入 CJS:
// app.mjs (ESM)
import pkg from "lodash";   // lodash 是 CJS
console.log(pkg.camelCase("hello world"));
👉 Node.js 會自動幫你把 CJS 的 module.exports 當成 default export。
所以在 ESM 裡匯入 CJS,要用 import pkg from ...,而不是 {}。
在 CJS 專案裡引入 ESM:
// app.cjs
(async () => {
  const esm = await import("./esm.mjs");
  console.log(esm.hello()); 
})();
👉 在 CJS 專案裡引入 ESM,只能用 import(),而且要在 非同步環境下。
| 功能 | CommonJS (CJS) | ES Modules (ESM) | 
|---|---|---|
| 匯入 | const x = require('./x') | import x from './x.js' | 
| 匯出(單一) | module.exports = foo | export default foo | 
| 匯出(多個) | exports.a = 1 | export const a = 1 | 
| 動態載入 | require("./x") | const x = await import("./x.js") | 
Node.js 正在逐漸與 瀏覽器標準接軌:
require / module.exports。fetch、URL、TextEncoder 已在 Node.js 內建,與瀏覽器一致。未來的 Node.js 會越來越像瀏覽器,你只要會 JavaScript,在前端怎麼寫,後端幾乎也能照樣寫。
今天學到: